// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2025-01-08

package container

import (
	"gitee.com/go-libs/perm/types"
)

type (
	// ResourceContainer is a component interface that store resource,
	// permissions. It is used to verify whether one of given roles is
	// authorized to the resource.
	ResourceContainer interface {
		// Authorize adds a role to authentications. Users can access the
		// resource if one of user roles is equal to the role.
		Authorize(typ types.Role)

		// GetAccessible return resource accessible.
		GetAccessible() types.Accessible

		// GetPermission return resource permission.
		GetPermission() types.Permission

		// GetResource return resource type.
		GetResource() types.Resource

		// SetAccessible set resource accessible.
		SetAccessible(typ types.Accessible)

		// SetPermission set resource permission.
		SetPermission(typ types.Permission)

		// Verify whether one of the roles in the given list is authorized,
		// Return success verification if any role in the authentications.
		Verify(roles ...types.Role) types.Verification
	}

	resource struct {
		Accessible types.Accessible
		Permission types.Permission
		Resource   types.Resource
		Roles      map[types.Role]string
	}
)

func NewResourceContainer(name types.Resource) ResourceContainer {
	return &resource{
		Accessible: types.AccessibleEmpty, Resource: name,
		Roles: make(map[types.Role]string),
	}
}

func (o *resource) Authorize(typ types.Role) { o.Roles[typ] = typ.String() }

func (o *resource) GetAccessible() types.Accessible { return o.Accessible }
func (o *resource) GetPermission() types.Permission { return o.Permission }
func (o *resource) GetResource() types.Resource     { return o.Resource }

func (o *resource) SetAccessible(typ types.Accessible) { o.Accessible = typ }
func (o *resource) SetPermission(typ types.Permission) { o.Permission = typ }

func (o *resource) Verify(roles ...types.Role) types.Verification {
	// The resource is public, all users can access it.
	if o.Accessible.IsYes() {
		return types.VerificationAccessible
	}

	// One of given role in list is authorized.
	for _, r := range roles {
		if _, ok := o.Roles[r]; ok {
			return types.VerificationAuthorized
		}
	}

	// All given roles are not authorized.
	return types.VerificationUnauthorized
}
